package config

import (
	"bytes"
	"fmt"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/fatih/color"
	"github.com/spf13/viper"
	"nodemessage.com/codetool/logic"
	"nodemessage.com/codetool/model"
	"nodemessage.com/codetool/util/fileutil"
	"os"
	"path"
)

type ConfGenCodeForm struct {
	SelectItem []string
	Cursor     int
	Selected   map[int]string
}

func (m ConfGenCodeForm) Init() tea.Cmd {
	return nil
}

func (m ConfGenCodeForm) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "enter":
			return m, tea.Quit
		case "up", "k":
			if m.Cursor > 0 {
				m.Cursor--
			}
		case "down", "j":
			if m.Cursor < len(m.SelectItem)-1 {
				m.Cursor++
			}
		case " ":
			_, ok := m.Selected[m.Cursor]
			if ok {
				delete(m.Selected, m.Cursor)
			} else {
				m.Selected[m.Cursor] = m.SelectItem[m.Cursor]
			}
		}
	}
	return m, nil
}

func (m ConfGenCodeForm) View() string {
	s := "\n请按 [空格键] 选择要选择生成的内容:\n\n"
	for i, choice := range m.SelectItem {
		cursor := " "
		if m.Cursor == i {
			cursor = ">"
		}
		checked := " "
		if _, ok := m.Selected[i]; ok {
			checked = color.New(color.FgHiBlue, color.BgHiWhite, color.Bold).Sprintf("Y")
		}
		s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
	}
	s += "\n请按回车确认.\n"
	return s
}

var (
	proJavaPath     = path.Join("src", "main", "java")
	proResourcePath = path.Join("src", "main", "resources")
	proJavaTestPath = path.Join("src", "test", "java")
	ConfigFile      = "gencode.yaml"
)

func ConfGenCode(projectName string) *viper.Viper {
	genPro := genPro(projectName)
	config := checkConfig()
	if genPro {
		config.ProjectName = projectName
		config.BasePath = proJavaPath
		ConfigFile = path.Join(projectName, ConfigFile)
		pomContent, err := genPomFile(config)
		if err != nil {
			panic(err)
		}
		// 内容写入
		pomPath := path.Join(projectName, "pom.xml")
		if err = fileutil.WriteToFile(pomPath, pomContent); err != nil {
			panic(err)
		}
		if applicationTemplate, err := logic.GetVmFs("vm/application.yml.vm"); err != nil {
			panic(err)
		} else {
			applicationPath := path.Join(projectName, proResourcePath, "application.yml")
			buffer := bytes.Buffer{}
			err = applicationTemplate.Execute(&buffer, struct{}{})
			if err = fileutil.WriteToFile(applicationPath, buffer.Bytes()); err != nil {
				panic(err)
			}
		}
		if applicationDevTemplate, err := logic.GetVmFs("vm/application-dev.yml.vm"); err != nil {
			panic(err)
		} else {
			applicationDevPath := path.Join(projectName, proResourcePath, "application-dev.yml")
			buffer := bytes.Buffer{}
			err = applicationDevTemplate.Execute(&buffer, struct{}{})
			if err = fileutil.WriteToFile(applicationDevPath, buffer.Bytes()); err != nil {
				panic(err)
			}
		}
	}
	if config != nil {
		file, err := os.OpenFile(ConfigFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
		if err != nil {
			panic(err)
		}
		file.Close()
	}
	confGenCode := viper.New()
	confGenCode.SetConfigType("yaml")
	confGenCode.SetConfigFile(ConfigFile)
	confGenCode.AddConfigPath(".")
	if err := confGenCode.ReadInConfig(); err != nil {
		panic(fmt.Errorf("read gencode.yaml file error: %s \n", err.Error()))
	}
	if config != nil {
		confGenCode.Set("meta", config.JavaMeta)
		confGenCode.Set("db", config.Db)
		err := confGenCode.WriteConfig()
		if err != nil {
			panic(err)
		}
	}
	return confGenCode
}

func checkConfig() *model.ConfGenCode {
	stat, _ := os.Stat(ConfigFile)
	if stat == nil {
		author := ""
		fmt.Printf("\n> 作者:")
		_, err := fmt.Scanf("%s\n", &author)
		if err != nil {
			panic(err)
		}
		fmt.Printf("> 包名:")
		packageName := ""
		_, err = fmt.Scanf("%s\n", &packageName)
		if err != nil {
			panic(err)
		}
		var initModel = ConfGenCodeForm{
			SelectItem: []string{"实体类", "Dao类", "Service类", "Service实现类", "Controller类"},
			Selected:   make(map[int]string),
		}
		cmd := tea.NewProgram(initModel)
		_, err = cmd.Run()
		if err != nil {
			panic(err)
		}
		// 配置文件
		entityGen := false
		if _, ok := initModel.Selected[0]; ok {
			entityGen = true
		}
		daoGen := false
		if _, ok := initModel.Selected[1]; ok {
			daoGen = true
		}
		serviceGen := false
		if _, ok := initModel.Selected[2]; ok {
			serviceGen = true
		}
		serviceImplGen := false
		if _, ok := initModel.Selected[3]; ok {
			serviceImplGen = true
		}
		controllerGen := false
		if _, ok := initModel.Selected[4]; ok {
			controllerGen = true
		}
		conf := new(model.ConfGenCode)
		meta := logic.JavaMeta{
			Author:      author,
			BasePackage: packageName,
			BasePath:    ".",
			Entity: logic.MetaGen{
				Gen:     entityGen,
				Package: "entity",
				Suffix:  "Entity",
			},
			Dao: logic.MetaGen{
				Gen:     daoGen,
				Package: "dao",
				Suffix:  "Dao",
			},
			Service: logic.MetaGen{
				Gen:     serviceGen,
				Package: "service",
				Suffix:  "Service",
			},
			ServiceImpl: logic.MetaGen{
				Gen:     serviceImplGen,
				Package: "-",
				Suffix:  "-",
			},
			Controller: logic.MetaGen{
				Gen:     controllerGen,
				Package: "controller",
				Suffix:  "Controller",
			},
		}
		conf.JavaMeta = meta
		fmt.Printf("\n> 数据库Ip地址:")
		_, err = fmt.Scanf("%s\n", &conf.Db.Host)
		if err != nil {
			panic(err)
		}
		fmt.Printf("\n> 数据库端口:")
		_, err = fmt.Scanf("%d\n", &conf.Db.Port)
		if err != nil {
			panic(err)
		}
		fmt.Printf("\n> 数据库用户名:")
		_, err = fmt.Scanf("%s\n", &conf.Db.UserName)
		if err != nil {
			panic(err)
		}
		fmt.Printf("\n> 数据库密码:")
		_, err = fmt.Scanf("%s\n", &conf.Db.Password)
		if err != nil {
			panic(err)
		}
		fmt.Printf("\n> 数据库名:")
		_, err = fmt.Scanf("%s\n", &conf.Db.DataBase)
		if err != nil {
			panic(err)
		}
		fmt.Println()
		fmt.Println("> 初始化成功,请在当前目录下的 [" + ConfigFile + "] 文件中配置信息\n")
		return conf
	}
	return nil
}

func genPro(projectName string) (genPro bool) {
	if projectName == "" {
		return
	}
	if err := fileutil.MkdirPath(projectName); err != nil {
		panic(err)
	}
	if err := fileutil.MkdirPath(path.Join(projectName, proJavaPath)); err != nil {
		panic(err)
	}
	if err := fileutil.MkdirPath(path.Join(projectName, proResourcePath)); err != nil {
		panic(err)
	}
	if err := fileutil.MkdirPath(path.Join(projectName, proJavaTestPath)); err != nil {
		panic(err)
	}
	return true
}

func genPomFile(conf *model.ConfGenCode) ([]byte, error) {
	pomTemplate, err := logic.GetVmFs("vm/pom.xml.vm")
	if err == nil {
		buffer := bytes.Buffer{}
		err = pomTemplate.Execute(&buffer, conf)
		return buffer.Bytes(), err
	}
	return nil, err
}
